Skip to content

fix(ios): never default to a connected device on plain run-ios#2795

Open
azizbecha wants to merge 1 commit intoreact-native-community:mainfrom
azizbecha:fix/run-ios-prefer-simulator-over-device
Open

fix(ios): never default to a connected device on plain run-ios#2795
azizbecha wants to merge 1 commit intoreact-native-community:mainfrom
azizbecha:fix/run-ios-prefer-simulator-over-device

Conversation

@azizbecha
Copy link
Copy Markdown
Collaborator

Summary

Fixes #2765 (continuation of #2254).

When react-native run-ios is invoked without any of --device / --udid / --simulator, it should target a simulator (per the docs: "Builds your app and starts it on iOS simulator."). In practice the default path was installing the app on a connected physical iPhone instead. Modern Xcode no longer lets users disable Wi-Fi device pairing, so any developer who has ever paired an iPhone hits this every run.

Root cause

packages/cli-platform-apple/src/commands/runCommand/createRun.ts, the no-flag branch:

const bootedSimulators = devices.filter(
  ({state, type}) => state === 'Booted' && type === 'simulator',
);
const bootedDevices = devices.filter(({type}) => type === 'device'); // treats every connected iPhone as "booted"
const booted = [...bootedSimulators, ...bootedDevices];
// ...
for (const device of bootedDevices) { await runOnDevice(...); } // wrong: installs on the phone with no opt-in

Two problems:

  1. bootedDevices participates in the no-flag default path at all.
  2. When the user hasn't booted a simulator (very common), booted.length !== 0 because of the iPhone, so the fallback-simulator branch is skipped and the build/install runs on the device — exactly the trace from the issue:
    info Found booted Stefan's iPhone 16
    info Building (using "xcodebuild -workspace ... -destination id=00008140-…")
    info Installing and launching your app on Stefan's iPhone 16
    

Fix

Replace the no-flag block with simulator-only logic:

  • Pick the first booted simulator, otherwise the existing fallbackSimulator (computed earlier via getFallbackSimulator(args)).
  • Call runOnSimulator exactly once with that target.
  • If any physical device was detected, log a single info hint naming the device(s) and pointing to --device / --udid, so users understand the change.

Explicit paths (--device, --udid, --simulator, --list-devices, --interactive) are unchanged — the fix only affects the no-flag default.

Test Plan

Unit tests (added)

packages/cli-platform-apple/src/commands/runCommand/__tests__/createRun.test.ts covers:

  1. iPhone connected + no booted simulator + no flagsrunOnSimulator is called once with the fallback simulator; runOnDevice is not called. (Direct repro of run-ios prioritizes devices over simulators, contradicting docs #2765.)
  2. iPhone connected + booted simulator + no flagsrunOnSimulator is called once with the booted simulator; runOnDevice is not called.
  3. iPhone connected + --device "<name>"runOnDevice is called with the iPhone (regression guard for the explicit path).
  4. No physical devices, no flagsrunOnSimulator is called once with the fallback simulator (preserves prior behavior).
yarn jest --selectProjects unit packages/cli-platform-apple
# Test Suites: 55 passed, 55 total
# Tests:       1 todo, 295 passed, 296 total

yarn lint and yarn build are also green.

Manual repro

Using https://github.com/swrobel/cli-reproducer:

  • iPhone paired in Xcode, no simulator booted, run npx react-native run-ios → expect a simulator to launch (e.g. iPhone 14 / first available), not the iPhone. The output now includes Ignoring connected device (<iPhone name>). Pass --device or --udid to install on a physical device.
  • Boot a simulator, repeat → expect the app to install on the booted simulator only.
  • Run npx react-native run-ios --device "<iPhone name>" → expect the iPhone path to still work end-to-end.

Checklist

  • Documentation is up to date (the existing cli-platform-ios/README.md already describes this behavior — the fix brings code in line with the docs).
  • Follows commit message convention described in CONTRIBUTING.md.
  • For functional changes, my test plan has linked these CLI changes into a local react-native checkout — manual repro listed above; happy to add screenshots if useful.

…-native-community#2765)

When neither --device nor --udid nor --simulator is supplied, run-ios
now always targets a simulator (booted preferred, otherwise the
fallback simulator) and ignores any connected physical device. The
previous default counted physical devices as "booted" and installed the
app on the iPhone alongside (or instead of) a simulator, which
contradicted the docs and was unavoidable in modern Xcode where Wi-Fi
device pairing cannot be disabled. Physical devices are still reachable
via --device / --udid.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

run-ios prioritizes devices over simulators, contradicting docs

1 participant